<template>
  <div>
    <!-- 面包屑导航区 -->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>商品管理</el-breadcrumb-item>
      <el-breadcrumb-item>编辑商品</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 卡片视图区域 -->
    <el-card>
      <!-- 提示区 -->
      <el-alert
        title="编辑商品信息"
        type="info"
        center
        show-icon
        :closable="false"
      >
      </el-alert>
      <!-- 进度条 为让steps和tab-pane实现一一对应联动，都绑定上了activeIndex，但steps这里要的是数字类型，所以最简单来-0处理-->
      <el-steps
        :space="200"
        :active="activeIndex - 0"
        finish-status="success"
        align-center
      >
        <el-step title="基本信息"></el-step>
        <el-step title="商品参数"></el-step>
        <el-step title="商品属性"></el-step>
        <el-step title="商品图片"></el-step>
        <el-step title="商品内容"></el-step>
        <el-step title="完成"></el-step>
      </el-steps>
      <!-- label-position-top让标签在输入框上面 -->
      <el-form
        :model="editGoodsForm"
        status-icon
        :rules="editFormRules"
        ref="editGoodsFormRef"
        label-width="100px"
        label-position="top"
      >
        <!-- v-model绑定的是被激活的tab-pane的name属性 tab-pane被激活的name更新给activeIndex,activeIndex又更新给steps,实现联动-->
        <!-- 只不过tab-pane的name值期望是字符串类型，但el-steps的active想要的是数值类型，所以el-steps在取值时要转换下类型 -->
        <el-tabs
          tab-position="left"
          style="height: auto"
          v-model="activeIndex"
          :before-leave="beforeTabLeave"
          @tab-click="tabClick"
        >
          <!-- el-tab-pane只允许做el-tabs的子节点,所以form表单得在外面-->
          <el-tab-pane label="基本信息" name="0">
            <el-form-item label="商品名称" prop="goods_name">
              <el-input v-model="editGoodsForm.goods_name"></el-input>
            </el-form-item>
            <el-form-item label="商品价格" prop="goods_price">
              <el-input
                type="number"
                v-model="editGoodsForm.goods_price"
              ></el-input>
            </el-form-item>
            <el-form-item label="商品重量" prop="goods_weight">
              <el-input
                type="number"
                v-model="editGoodsForm.goods_weight"
              ></el-input>
            </el-form-item>
            <el-form-item label="商品数量" prop="goods_number">
              <el-input
                type="number"
                v-model="editGoodsForm.goods_number"
              ></el-input>
            </el-form-item>
            <el-form-item label="商品分类" prop="goods_cat">
              <el-cascader
                v-model="editGoodsForm.goods_cat"
                :options="goodCateList"
                :props="cascaderProps"
                @change="handleChange"
              ></el-cascader>
            </el-form-item>
          </el-tab-pane>
          <el-tab-pane label="商品参数" name="1">
            <el-form-item
              :label="item.attr_name"
              v-for="item in manyParamsData"
              :key="item.attr_id"
            >
              <!-- 复选框组 -->
              <el-checkbox-group v-model="item.attr_vals">
                <el-checkbox
                  :label="value"
                  v-for="(value, i) in item.attr_vals"
                  :key="i"
                  border
                ></el-checkbox>
              </el-checkbox-group>
            </el-form-item>
          </el-tab-pane>
          <el-tab-pane label="商品属性" name="2">
            <el-form-item
              :label="item.attr_name"
              v-for="item in onlyParamsData"
              :key="item.attr_id"
            >
              <el-input type="text" v-model="item.attr_vals"></el-input>
            </el-form-item>
          </el-tab-pane>
          <el-tab-pane label="商品图片" name="3">
            <!-- action表示图片要上传到的后台地址，不能写相对路径，应该是完整地址，不然会被认为是8080下的 -->
            <!-- list-type表示上传的图片的展示方式 -->
            <el-upload
              :action="uploadURL"
              :file-list="picFileList"
              :on-preview="handlePreview"
              :on-remove="handleRemove"
              list-type="picture"
              :headers="headerObj"
              :on-success="uploadPicSuccess"
              :before-upload="beforePicUpload"
            >
              <el-button size="small" type="primary">点击上传</el-button>
              <div slot="tip" class="el-upload__tip">
                只能上传jpg/png文件，且不超过2M
              </div>
            </el-upload>
          </el-tab-pane>
          <el-tab-pane label="商品描述" name="4">
            <!-- 富文本编辑器 -->
            <quill-editor v-model="editGoodsForm.goods_introduce">
            </quill-editor>
            <el-button type="primary" class="btnAddGoods" @click="addGoodInfo"
              >提交修改</el-button
            >
          </el-tab-pane>
        </el-tabs>
      </el-form>
    </el-card>
    <!-- 图片预览的对话框 -->
    <el-dialog title="图片预览" :visible.sync="dialogVisible" width="50%">
      <img :src="previewPath" class="previewImg" />
    </el-dialog>
  </div>
</template>

<script>
// 借助lodash的深拷贝，因为本地中商品所属分类的数据是一个数组，为了迎合级联选择器；但是提交添加请求前得把数据转为字符串类型去迎合服务器。如果直接把转完的结果赋给本身可能级联选择器的数据绑定那会报错（现在新版的可能是官方取消了这个数据绑定的类型），深拷贝出一个商品信息对象，操作拷贝对象而对原数据无影响
import _ from 'lodash'
export default {
  data() {
    return {
      // 进度条的当前索引
      activeIndex: '0',
      // 从原型中获取当前的商品id
      goodsID: this.editGoodsID.id,
      // 编辑商品的表单数据对象
      editGoodsForm: {},
      // 编辑表单中的验证规则对象
      editFormRules: {
        goods_name: [
          { required: true, message: '商品名称不能为空！', trigger: 'blur' },
        ],
        goods_price: [
          { required: true, message: '商品价格不能为空！', trigger: 'blur' },
        ],
        goods_weight: [
          { required: true, message: '商品重量不能为空！', trigger: 'blur' },
        ],
        goods_number: [
          { required: true, message: '商品数量不能为空！', trigger: 'blur' },
        ],
        goods_cat: [
          { required: true, message: '商品分类不能为空！', trigger: 'blur' },
        ],
      },
      // 商品分类数据
      goodCateList: [],
      // 级联选择器的配置项
      cascaderProps: {
        children: 'children',
        value: 'cat_id',
        label: 'cat_name',
      },
      // 保存请求到的动态参数
      manyParamsData: [],
      // 保存请求到的静态属性
      onlyParamsData: [],
      // 上传图片的url地址
      uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload',
      // 图片上传组件的headerObj请求头对象，上传不成功因为el-upload没有走axios发Ajax请求，而是自己内部封装了一套Ajax，这套Ajax没有Authorization字段，所以也就跟token没关系，网络请求也就会被响应token无效
      headerObj: {
        // 每次一上传图片时都会给请求头中加上一个Authorization，并把token赋值
        Authorization: window.sessionStorage.getItem('token'),
      },
      // 存储要预览的图片的完整路径
      previewPath: '',
      // 图片预览对话框的显示与隐藏
      dialogVisible: false,
      // 图片数据列表
      picFileList: [],
    }
  },
  created() {
    // 获取商品所有数据
    this.getGoodData()
    // 获取所有商品的分类数据
    this.getCateList()
  },
  methods: {
    // 获取商品所有信息数据
    async getGoodData() {
      const { data: res } = await this.$http.get(`goods/${this.goodsID}`)
      if (res.meta.status !== 200) {
        this.$message.error('获取商品信息数据失败！')
        // 没获取到就给它转走，转到商品列表页
        this.$router.push('/goods')
      } else {
        this.editGoodsForm = res.data
        // 将级联选择器需要的数据从服务器的字符串转为数组
        this.editGoodsForm.goods_cat = this.editGoodsForm.goods_cat.split(',')
        // 将服务器给的数据中的图片信息（已有的）格式化为图片上传中依赖的文件列表类型
        if (this.editGoodsForm.pics.length > 0) {
          for (let i = 0; i < this.editGoodsForm.pics.length; i++) {
            this.picFileList.push({
              name: this.editGoodsForm.pics[i].pics_big.split('/')[3].slice(4),
              url: this.editGoodsForm.pics[i].pics_big_url,
            })
          }
          // 将editGoodsForm的pics置空，当一张白纸，后面有添加成功的自然追加进去，已经存在的从picFileList中导过去
          this.editGoodsForm.pics.length = 0
          for (let i = 0; i < this.picFileList.length; i++) {
            const picStr = this.picFileList[i].name
            this.editGoodsForm.pics.push({ pic: 'tmp_uploads\\' + picStr })
          }
        }
        // console.log(this.editGoodsForm.pics)
      }
    },
    // 获取所有商品的分类数据
    async getCateList() {
      const { data: res } = await this.$http.get('categories')
      if (res.meta.status !== 200) {
        return this.$message.error('获取商品分类数据失败')
      } else {
        this.goodCateList = res.data
      }
    },
    // 级联选择器选中项变化时会触发的函数--只允许选第三层级
    handleChange() {
      if (this.editGoodsForm.goods_cat.length !== 3) {
        this.$message.warning('只允许选择第三层级的分类！')
        this.editGoodsForm.goods_cat.length = 0
      }
    },
    // 在tab标签切换时由tabs的before-leave钩子调用，默认的两个参数分别是新的和当前的tab标签的name属性
    beforeTabLeave(newTabName, oldTabName) {
      if (oldTabName == 0 && this.editGoodsForm.goods_cat.length !== 3) {
        this.$message.error('您还没有为这个商品选择分类！')
        return false
        /* }else if(oldTabName == 0){
        // 从基本信息面板切换到其他面板前做下校验
        if(this.addFormValid()!=true){
          return false
        } */
      }
    },
    // 选中tab标签时
    async tabClick() {
      // 借助tabs绑定的activeIndex来判断当前被激活的标签name，如果是1就发起获取动态参数的请求
      if (this.activeIndex == 1) {
        const { data: res } = await this.$http.get(
          `categories/${this.cateID}/attributes`,
          { params: { sel: 'many' } }
        )
        console.log(res.data)
        if (res.meta.status !== 200) {
          this.$message.error('获取动态参数失败！')
        } else {
          console.log(res.data)
          // 对响应数据中的字符串处理成数组，结构中的多选框再根据这个数组去渲染
          res.data.forEach((item) => {
            item.attr_vals =
              item.attr_vals.length == 0 ? [] : item.attr_vals.split(' ')
          })

          // 将数据保存下来以供页面渲染tag
          this.manyParamsData = res.data
          // console.log(this.manyParamsData)
          this.$message.success('获取动态参数成功！')
        }
      } else if (this.activeIndex == 2) {
        // 如果选中的是商品属性面板,发起请求获取静态属性数据
        const { data: res } = await this.$http.get(
          `categories/${this.cateID}/attributes`,
          { params: { sel: 'only' } }
        )
        // console.log(res.data)
        if (res.meta.status !== 200) {
          this.$message.error('获取静态属性失败！')
        } else {
          this.onlyParamsData = res.data
          console.log(this.onlyParamsData)
          this.$message.success('获取静态参数成功！')
        }
      }
    },
    // 图片上传前对其进行大小及格式检查
    beforePicUpload(file) {
      var houzhui = file.name.substring(file.name.lastIndexOf('.') + 1)
      const extension =
        houzhui === 'jpg' ||
        houzhui === 'JPG' ||
        houzhui === 'png' ||
        houzhui === 'PNG'
      const isLt2M = file.size / 1024 / 1024 < 2
      if (!extension) {
        this.$message({
          message: '上传图片只能是jpg / png 格式!',
          type: 'error',
        })
        return false //必须加上return false; 才能阻止
      }
      if (!isLt2M) {
        this.$message({
          message: '上传文件大小不能超过 2MB!',
          type: 'warning',
        })
        return false
      }
      return extension || isLt2M
    },
    // 图片预览事件--形参默认是图片信息
    handlePreview(file) {
      // 将完整路径存储起来供给后面的弹窗预览做数据源
      this.previewPath = file.url
      // 开启预览弹窗
      this.dialogVisible = true
    },
    // 监听图片上传成功的事件---上传成功后将服务器返回的这个图片的临时路径保存到添加商品的表单中
    uploadPicSuccess(response) {
      console.log('success的response', response)
      // 从服务器响应的数据中提取这个图片的信息
      const picInfo = { pic: response.data.tmp_path }
      // 将这个信息追加到addGoodsForm的pics数组中，以备最后统一发送添加请求
      this.editGoodsForm.pics.push(picInfo)
    },
    // 移除图片时--由于做了图片格式和大小的检查，所以不通过检查会自动调用删除函数，但不通过检查的文件的status是ready,通过的是success,为了避免自动调用删除函数引起报错内部的data无效定义，先做条件判断再决定是否执行删除函数
    handleRemove(file) {
      console.log(file)
      //如果是新添加的存在response.data.tmp_path；
      //如果是删除之前已上传过的是没有这个临时路径的，需要自行格式化一下整出这个图片对应的临时路径字符串
      if (file && file.status === 'success') {
        if (file.response === undefined) {
          // 之前添加过的
          const filePath = 'tmp_uploads\\' + file.name
          const i = this.editGoodsForm.pics.findIndex((arrItem) => {
            arrItem.pic === filePath
          })
          // 调用splice对存储图片信息的数组pics按索引移除元素
          this.editGoodsForm.pics.splice(i, 1)
        } else {
          // 先获取到这个图片的临时路径
          const filePath = file.response.data.tmp_path
          // 从addGoodsForm的pics中查找这个图片的索引
          const i = this.editGoodsForm.pics.findIndex((arrItem) => {
            arrItem.pic === filePath
          })
          // 调用splice对存储图片信息的数组pics按索引移除元素
          this.editGoodsForm.pics.splice(i, 1)
        }
      }
    },
    // 点击添加商品按钮
    addGoodInfo() {
      this.$refs.editGoodsFormRef.validate(async (valid) => {
        if (!valid) {
          return this.$message.error('有必要的表单项没有填写！')
        } else {
          // 执行添加的业务逻辑--借助lodash而不直接赋值，赋值是浅拷贝，地址指向一致，会影响原数据
          const form = _.cloneDeep(this.editGoodsForm)
          form.goods_cat = form.goods_cat.join(',')
          // 处理动态参数--只提取服务器需要的id和value(value服务器接收的是字符串)
          this.manyParamsData.forEach((item) => {
            const newInfo = {
              attr_id: item.attr_id,
              attr_value: item.attr_vals.join(' '),
            }
            this.editGoodsForm.attrs.push(newInfo)
          })
          // 处理静态属性
          this.onlyParamsData.forEach((item) => {
            const newInfo = {
              attr_id: item.attr_id,
              attr_value: item.attr_vals,
            }
            this.editGoodsForm.attrs.push(newInfo)
          })
          // form由const而来，不允许被修改，但允许修改属性，所以上面直接对form进行追加
          form.attrs = this.editGoodsForm.attrs
          // 最后提交深拷贝的form
          // 发起添加商品的请求
          const { data: res } = await this.$http.put(
            `goods/${this.goodsID}`,
            form
          )
          if (res.meta.status !== 200) {
            return this.$message.error('更新商品数据失败！')
          } else {
            this.$message.success('更新商品数据成功！')
            // 通过编程式路由导航再跳回商品列表页面
            this.$router.push('/goods')
          }
        }
      })
    },
  },
  computed: {
    // 为了简化选中参数的tab后发起请求时传入的id的获取过程
    cateID() {
      if (this.editGoodsForm.goods_cat.length == 3) {
        return this.editGoodsForm.goods_cat[2]
      } else {
        return null
      }
    },
  },
}
</script>

<style lang="less" scoped>
.el-steps {
  margin: 15px 0;
}
.el-checkbox {
  margin: 0 10px 0 0 !important;
}
.previewImg {
  width: 100%;
}
.btnAddGoods {
  margin-top: 15px;
}
</style>